home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK2.toast / Development Kits (Disc 2) / QuickDraw GX / Programming Stuff / Sample Code / Printing Samples / Extensions… / Spooling ƒ / Spooling.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-10  |  23.1 KB  |  849 lines  |  [TEXT/MPS ]

  1. /*________________________________________________________
  2.  
  3.     File: Spooling.c
  4.     
  5.     This is the C source for a printing extension
  6.     that redirects spool files.
  7.     
  8.     Dave Hersey
  9.     Apple Developer Technical Support
  10.  
  11.      1/29/93 - dmh - Begat.
  12.      4/26/93 - dmh - Updated for b1.
  13.              - Switched to using the recommended
  14.                approach for initializing global data.
  15.      9/06/93 - dmh - Updated for b2.
  16.              - Switched to Exception.h assertion stuff
  17.                for error checking.
  18.     12/18/93 - dmh - Updated for b3.
  19.      3/22/94 - dmh - Updated for b4.
  20.  
  21.     (Note: all functions are in the Mark menu)
  22.     
  23. __________________________________________________________*/
  24.  
  25. #include "Spooling.h"
  26.  
  27. // globals:
  28.  
  29. short        gPrefsVRefNum;            // The volume it resides on.  (For FlushVol.)
  30. AliasHandle    gCurFolderAlias;        // The current folder alias used by the panel.
  31. Boolean        gSettingsChanged;        // True if we have panel changes to save.
  32. Boolean        gLastOnOffSetting;        // The "extTurnedOn" setting when we opened the panel.
  33.  
  34.  
  35. /*******************************************************************
  36.     InitGlobalData is used to initialize any global data we need to
  37.     in our initialize message override.  It's critical that you do
  38.     things this way, rather than access the data in the same scope
  39.     that you call NewMessageGlobals.  Otherwise, some compilers
  40.     will give you code that references an invalid A5 world.
  41.  
  42. ********************************************************************/
  43.  
  44. OSErr InitGlobalData()
  45. {
  46.     gCurFolderAlias = nil;
  47.     return noErr;
  48. }
  49.  
  50.  
  51. /*******************************************************************
  52.     SPInitialize is our override for GXInitialize.  We set up our
  53.     A5 world and jump off to initialize our global data.
  54.  
  55. ********************************************************************/
  56.  
  57. OSErr SPInitialize()
  58. {
  59.     OSErr    err;
  60.     
  61.     err = NewMessageGlobals(A5Size(), A5Init);
  62.     if (!err) err = InitGlobalData();
  63.  
  64.     return err;
  65. }
  66.  
  67.  
  68. /*******************************************************************
  69.     SPShutDown is our override for GXShutDown.  It just frees up
  70.     the A5 world we created in SPInitialize.
  71.  
  72. ********************************************************************/
  73.  
  74. OSErr SPShutDown()
  75. {
  76.     DisposeMessageGlobals();
  77.     return noErr;
  78. }
  79.  
  80.  
  81. /*******************************************************************
  82.     SPJobPrintDialog is our override for GXJobPrintDialog.  All we
  83.     do is set up our panel and then forward the message.
  84.  
  85. ********************************************************************/
  86.  
  87. OSErr SPJobPrintDialog(gxDialogResult *dlogResult)
  88. {
  89.     OSErr    err;
  90.     
  91.     err = SetUpPrintPanel();
  92.  
  93.     if (!err)
  94.         err = Forward_GXJobPrintDialog(dlogResult);
  95.     
  96.     return err;
  97. }
  98.  
  99.  
  100. /*******************************************************************
  101.     SetUpPrintPanel sets up our print panel, adding a default
  102.     SpoolCollection item to the job collection with our previous
  103.     settings.  This collection item has the values we'll use to
  104.     set up our panel's controls.
  105.  
  106. ********************************************************************/
  107.  
  108. OSErr SetUpPrintPanel()
  109. {
  110.     OSErr                    err;
  111.     gxPanelSetupRecord        panelSetupRec;
  112.     SpoolCollection            spoolConfig;
  113.  
  114. // Try to find our collection item.  We also try to get the default settings so
  115. // that we make sure the Prefs file is available.  If it's not, a new file will
  116. // be created by GetDefaultSettings.
  117.  
  118.     err = GetDefaultSettings(&spoolConfig);
  119.     nrequire(err, CanNotLoadDefaults);
  120.  
  121.  
  122. // Store our settings in the job collection.
  123.  
  124.     err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  125.                             kSpoolCollectionType,
  126.                             gxPrintingTagID,
  127.                             sizeof(SpoolCollection),
  128.                             &spoolConfig);
  129.  
  130.     nrequire(err, HaveCollectionMgrError);
  131.  
  132.  
  133. // Now, do the actual panel set up.
  134.  
  135.     gSettingsChanged = false;
  136.     gLastOnOffSetting = spoolConfig.extTurnedOn;
  137.  
  138.     panelSetupRec.panelResId        = r_spoolPanel;            // which panel resource?
  139.     panelSetupRec.resourceRefNum    = GXGetMessageHandlerResFile();    // where is it?
  140.     panelSetupRec.refCon            = 0;                    // we don't use this.
  141.     panelSetupRec.panelKind            = gxExtensionPanel;        // This is an extension panel.
  142.     
  143.     err = GXSetupDialogPanel(&panelSetupRec);
  144.  
  145. HaveCollectionMgrError:
  146. CanNotLoadDefaults:
  147.  
  148.     return err;
  149. }
  150.  
  151.  
  152. /*******************************************************************
  153.     SPHandlePanelEvent is our override for GXHandlePanelEvent.  If
  154.     the event is one of ours, we handle it.  Otherwise, we just
  155.     forward it down the chain.
  156.  
  157. ********************************************************************/
  158.  
  159. OSErr SPHandlePanelEvent(gxPanelInfoRecord *panelInfo)
  160. {
  161.     OSErr                        err = noErr;
  162.     GrafPtr                        oldPort;
  163.     DialogPtr                    pDlg;
  164.     Handle                        hItem;
  165.     Rect                        itemRect;
  166.     short                        itemType;
  167.     short                        oldResFile;
  168.     SpoolCollection                spoolConfig;
  169.     FSSpec                        curFSpec;
  170.  
  171.     pDlg = panelInfo->pDlg;
  172.     GetPort(&oldPort);
  173.     SetPort(pDlg);
  174.     
  175.     switch (panelInfo->panelEvt)
  176.     {
  177.  
  178. // If our panel is opening, go do any initialization we need to.
  179.  
  180.         case gxPanelOpenEvt:
  181.             OpenSpoolPanel(pDlg, panelInfo->itemCount);
  182.             break;
  183.  
  184. // If the user hit the "Print" or "Save" buttons, go save any
  185. // changes we need to.
  186.  
  187.         case gxPanelConfirmEvt:
  188.             err = SavePanelChanges();
  189.             break;
  190.  
  191.  
  192. // If the user hits the "Spool to…" button, prompt them for a folder.
  193. // If they select one, get our old collection item, move the FSSpec info to it,
  194. // and replace the old collection item with our modified one.
  195.  
  196.         case gxPanelHitEvt:
  197.             if (panelInfo->itemHit == (panelInfo->itemCount + d_selectDirectory))
  198.             {
  199.                 oldResFile = CurResFile();
  200.                 UseResFile(GXGetMessageHandlerResFile());
  201.  
  202.                 require(GetFolder(&curFSpec), GetFolder_Failed);
  203.                 
  204.                 gSettingsChanged = true;
  205.                 err = GetSpoolCollection(&spoolConfig);
  206.                 nrequire(err, GetSpoolCollection_Failed);
  207.  
  208.                 if (gCurFolderAlias != nil)
  209.                 {
  210.                     DisposHandle((Handle) gCurFolderAlias);
  211.                     gCurFolderAlias = nil;
  212.                 }
  213.                 
  214.                 err = NewAlias(nil, &curFSpec, &gCurFolderAlias);
  215.                 nrequire(err, NewAlias_Failed);
  216.                 AliasToPathName(gCurFolderAlias, &spoolConfig.folderName, &spoolConfig.volumeName);
  217.                 err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  218.                                         kSpoolCollectionType,
  219.                                         gxPrintingTagID,
  220.                                         sizeof(SpoolCollection),
  221.                                         &spoolConfig);
  222.  
  223.                 nrequire(err, AddCollectionItem_Failed);
  224.  
  225.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_folderName, &itemType, &hItem, &itemRect);
  226.                 SetIText(hItem, &spoolConfig.folderName);
  227.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_volumeName, &itemType, &hItem, &itemRect);
  228.                 SetIText(hItem, &spoolConfig.volumeName);
  229.  
  230.  
  231. AddCollectionItem_Failed:
  232. NewAlias_Failed:
  233. GetSpoolCollection_Failed:
  234. GetFolder_Failed:
  235.  
  236.                 UseResFile(oldResFile);
  237.             }
  238.             break;
  239.     }
  240.     
  241.     SetPort(oldPort);
  242.     return err;
  243. }
  244.  
  245.  
  246. /*******************************************************************
  247.     OpenSpoolPanel handles non-'xdtl' item initialization when we
  248.     open our panel.  Note that our items will be offset from
  249.     itemCount.  (So, if we want item #5 in our DITL, we pass
  250.     itemCount +5 to GetDItem.)
  251.  
  252. ********************************************************************/
  253.  
  254. void OpenSpoolPanel(DialogPtr pDlg, short itemCount)
  255. {
  256.     SpoolCollection        spoolConfig;
  257.     Handle                hItem;
  258.     Rect                itemRect;
  259.     short                itemType;
  260.  
  261. // Initialize the current file name displayed, based on the
  262. // settings in our SpoolCollection item.
  263.  
  264.     GetSpoolCollection(&spoolConfig);
  265.     GetDItem(pDlg, itemCount +d_folderName, &itemType, &hItem, &itemRect);
  266.     SetIText(hItem, &spoolConfig.folderName);
  267.     GetDItem(pDlg, itemCount +d_volumeName, &itemType, &hItem, &itemRect);
  268.     SetIText(hItem, &spoolConfig.volumeName);
  269. }
  270.  
  271.  
  272. /*******************************************************************
  273.     SavePanelChanges saves our panel settings to disk when they
  274.     change and the user confirms our panel.  We only save the
  275.     settings if they've changed since the last save.
  276.  
  277. ********************************************************************/
  278.  
  279. OSErr SavePanelChanges()
  280. {
  281.     OSErr                err;
  282.     SpoolCollection        spoolConfig, **spoolCollHdl;
  283.     short                oldResFile, resRefNum;
  284.  
  285.     err = GetSpoolCollection(&spoolConfig);
  286.     nrequire(err, GetSpoolCollection_Failed);
  287.  
  288.  
  289. // If our settings haven't changed, exit.
  290.  
  291.     gSettingsChanged = (gSettingsChanged || (spoolConfig.extTurnedOn != gLastOnOffSetting));
  292.     require(gSettingsChanged, SettingsHaveNotChanged);
  293.  
  294.  
  295. // Open the preferences file and set it as our current resource file.
  296.  
  297.     oldResFile = CurResFile();
  298.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  299.     nrequire(err, OpenPrefsFile_Failed);
  300.     UseResFile(resRefNum);
  301.  
  302.  
  303. // Create a handle to store our settings in, move the settings to that handle,
  304. // and then store it to disk as a resource. Also store the folder alias as a
  305. // resource.  If we can do this without any errors occurring, update our
  306. // global flags.
  307.  
  308.     spoolCollHdl = (SpoolCollection**) NewHandle(sizeof(SpoolCollection));
  309.     nrequire((err = MemError()), NewHandle_Failed);
  310.  
  311.     BlockMove(&spoolConfig, *spoolCollHdl, sizeof(SpoolCollection));
  312.     err = ReplaceResource((Handle) spoolCollHdl, kConfigType, kConfigID);
  313.     DisposHandle((Handle) spoolCollHdl);
  314.     nrequire(err, ReplaceResource_Failed);
  315.  
  316.     err = ReplaceResource((Handle) gCurFolderAlias, rAliasType, kAliasID);
  317.     nrequire(err, ReplaceResource_Failed);
  318.     gSettingsChanged = false;
  319.     gLastOnOffSetting = spoolConfig.extTurnedOn;
  320.  
  321.  
  322. ReplaceResource_Failed:
  323. NewHandle_Failed:
  324.  
  325.     UseResFile(oldResFile);
  326.     CloseResFile(resRefNum);
  327.  
  328.  
  329. OpenPrefsFile_Failed:
  330. SettingsHaveNotChanged:
  331. GetSpoolCollection_Failed:
  332.  
  333.     return err;
  334. }
  335.  
  336.  
  337. /*******************************************************************
  338.     SPCreateSpoolFile is our override for GXCreateSpoolFile.  It
  339.     just gets our collection item, sees if we're supposed to do
  340.     anything, and if so, changes where we create the spool file
  341.     before forwarding the message.
  342.     
  343.     If we get an error, we ignore it and try to create the spool
  344.     file without redirection.  This is so we're more user-friendly
  345.     in the case of a glitch.  At least their file will be created.
  346.  
  347. ********************************************************************/
  348.  
  349. OSErr SPCreateSpoolFile(FSSpec * anFSSpec, long createOptions, gxSpoolFile *spFile)    
  350. {
  351.     OSErr                    err;
  352.     SpoolCollection            **spoolCollHdl;
  353.     short                    oldResFile, resRefNum;
  354.     AliasHandle                folderAlias;
  355.     FSSpec                    theFSSpec;
  356.     Boolean                    wasChanged;
  357.  
  358. // Get and open our preferences file's resoruce fork.
  359.  
  360.     oldResFile = CurResFile();
  361.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  362.     nrequire(err, OpenPrefsFile_Failed);
  363.     UseResFile(resRefNum);
  364.  
  365.  
  366. // Load the folder alias and extension settings we last saved.
  367.  
  368.     folderAlias = (AliasHandle) Get1Resource(rAliasType, kAliasID);
  369.     nrequire((err = ResError()), CouldNotLoadAlias);
  370.     DetachResource((Handle) folderAlias);
  371.  
  372.     spoolCollHdl = (SpoolCollection **) Get1Resource(kConfigType, kConfigID);
  373.     nrequire((err = ResError()), CouldNotLoadSettings);
  374.  
  375.  
  376. // If the user had usd turned on, try to resolve the alias.  If it gets updated,
  377. // save the new alias to disk.
  378.  
  379.     if ((*spoolCollHdl)->extTurnedOn)
  380.     {
  381.         err = ResolveAlias(nil, folderAlias, &theFSSpec, &wasChanged);
  382.         nrequire(err, ResolveAlias_Failed);
  383.  
  384.         if (wasChanged)
  385.             err = ReplaceResource((Handle) folderAlias, rAliasType, kAliasID);
  386.  
  387.         nrequire(err, ReplaceResource_Failed);
  388.         FSMakeFSSpec(theFSSpec.vRefNum, GetActualDirID(&theFSSpec), anFSSpec->name, &theFSSpec);
  389.         BlockMove(&theFSSpec, anFSSpec, sizeof(FSSpec));
  390.     }
  391.  
  392.  
  393. // Release the settings resource, dispose of the alias handle, reset our current
  394. // resource file, close the prefs file, and forward the message down the chain.
  395.  
  396. ReplaceResource_Failed:
  397. ResolveAlias_Failed:
  398.     
  399.     ReleaseResource((Handle) spoolCollHdl);
  400.  
  401.  
  402. CouldNotLoadSettings:
  403.     
  404.     DisposHandle((Handle) folderAlias);
  405.  
  406.  
  407. CouldNotLoadAlias:
  408.  
  409.     UseResFile(oldResFile);
  410.     CloseResFile(resRefNum);
  411.  
  412.  
  413. OpenPrefsFile_Failed:
  414.  
  415.     return Forward_GXCreateSpoolFile(anFSSpec, createOptions, spFile);
  416. }
  417.  
  418.  
  419. /*******************************************************************
  420.     GetFolder is a replacement for StandardGetFile which allows
  421.     us to select directories.
  422.  
  423. ********************************************************************/
  424.  
  425. Boolean GetFolder(FSSpec *fSpec)
  426. {
  427.     OSErr                err;
  428.     Point                where = {-1,-1};
  429.     StandardFileReply    sfReply;
  430.     short                foundVRefNum;
  431.     long                foundDirID;
  432.  
  433. // Have the user select a folder using the GetFile dialog.
  434.  
  435.     CustomGetFile((FileFilterYDProcPtr) FilterAllFiles, -1, nil, &sfReply, r_fileDlogID,
  436.                   where, MyDlgHook, nil, nil, nil, &sfReply);
  437.  
  438.     require(sfReply.sfGood, UserCancelledDialog);
  439.  
  440.  
  441. // If the user selected a volume, use the boot volume's desktop folder instead.
  442.  
  443.     nrequire(sfReply.sfIsVolume, UserDidNotChooseVolume);
  444.  
  445.     err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  446.     nrequire(err, FindFolder_Failed);
  447.  
  448.     err = FSMakeFSSpec(foundVRefNum, foundDirID, nil, fSpec);
  449.  
  450.  
  451. UserDidNotChooseVolume:
  452.     
  453.     err = FSMakeFSSpec(sfReply.sfFile.vRefNum, sfReply.sfFile.parID, nil, fSpec);
  454.  
  455.  
  456. // There appears to be a bug in the CustomGetFile stuff.  Sometimes the directory
  457. // returned is bogus.  It seems to only happen when the desktop folder should be
  458. // selected.  MPW demonstrates this same bug, so I don't think it's my code.
  459. //
  460. // Test: using a system with more than one volume attached, use MPW's "Set Directory"
  461. // dialog to highlight the desktop icon of a drive other than the boot drive.  Now
  462. // hit the "Select Current Directory" button.  All's well.  Go back and highlight a
  463. // folder alias that belongs to a DIFFERENT volume (or highlight nothing at all) at
  464. // the desktop folder level, and select that.  You get an error. (-120, "directory
  465. // not found").  That's what I was seeing here too.
  466. //
  467. // It appears that the standard file stuff is looking for the desktop folder of the
  468. // wrong volume.  As a work-around, I simply return the boot drive's desktop folder
  469. // if I get a "directory not found" error.
  470.  
  471.     if (err == dirNFErr)
  472.     {
  473.         err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  474.         nrequire(err, FindFolder_Failed);
  475.  
  476.         err = FSMakeFSSpec(foundVRefNum, foundDirID, nil, fSpec);
  477.     }
  478.  
  479.  
  480. FindFolder_Failed:
  481.  
  482.     if (err) sfReply.sfGood = false;
  483.  
  484.  
  485. UserCancelledDialog:
  486.  
  487.     return sfReply.sfGood;
  488. }
  489.  
  490.  
  491. /*******************************************************************
  492.     MyDlgHook is a dialog hook routine for CustomGetFile.
  493.  
  494. ********************************************************************/
  495.  
  496. pascal short MyDlgHook(short item, DialogPtr theDlg, Ptr userData)
  497. {
  498.     StandardFileReply    *curReply;
  499.     OSType                refCon;
  500.  
  501. // Unless we're being called from the main SF dialog, we don't do
  502. // anything.
  503.  
  504.     refCon = GetWRefCon(theDlg);
  505.     require((refCon == sfMainDialogRefCon), NotMainSFDialog);
  506.  
  507.  
  508. // If the user has selected the volume icon, change to the
  509. // desktop folder.  If they've selected our "Select Current Folder"
  510. // button, "OK" the dialog.
  511.  
  512.     curReply = (StandardFileReply *) userData;
  513.  
  514.     switch (item)
  515.     {
  516.         case sfItemVolumeUser:
  517.             curReply->sfFile.name[0] = '\0';
  518.             curReply->sfFile.parID = 2;
  519.             curReply->sfIsFolder = false;
  520.             curReply->sfIsVolume = false;
  521.             curReply->sfFlags = 0;
  522.             item = sfHookChangeSelection;
  523.             break;
  524.         
  525.         case d_selectItem:
  526.             item = sfItemOpenButton;
  527.             break;
  528.     }
  529.  
  530.  
  531. NotMainSFDialog:
  532.  
  533.     return item;
  534. }
  535.  
  536.  
  537. /*******************************************************************
  538.     FilterAllFiles is our CustomGetFile filterProc that only lets
  539.     directories be added to the display list.
  540.  
  541. ********************************************************************/
  542.  
  543. pascal Boolean FilterAllFiles(CInfoPBPtr pb, Ptr myDataPtr)
  544. {
  545. #pragma unused(myDataPtr);
  546.  
  547.      return (Boolean) !(pb->hFileInfo.ioFlAttrib & (1 <<4));
  548. }
  549.  
  550.  
  551. /*******************************************************************
  552.     GetActualDirID returns that actual directory ID (not the
  553.     parent's) of the folder referenced in the FSSpec * passed.
  554.  
  555. ********************************************************************/
  556.  
  557. long GetActualDirID(FSSpec * fSpec)
  558. {
  559.     DirInfo    infoPB;
  560.  
  561. // Get the passed folder's directory ID.
  562.  
  563.     infoPB.ioNamePtr = fSpec->name;
  564.     infoPB.ioVRefNum = fSpec->vRefNum;
  565.     infoPB.ioDrDirID = fSpec->parID;
  566.     infoPB.ioFDirIndex = 0;
  567.     PBGetCatInfo((CInfoPBPtr) &infoPB,false);
  568.  
  569.     return infoPB.ioDrDirID;
  570. }
  571.  
  572.  
  573. /*******************************************************************
  574.     GetDefaultSettings returns our previously saved settings.
  575.  
  576. ********************************************************************/
  577.  
  578. OSErr GetDefaultSettings(SpoolCollection *spoolConfig)
  579. {
  580.     OSErr                err;
  581.     long                foundDirID;
  582.     short                foundVRefNum, oldResRef, resRefNum;
  583.     SpoolCollection        **spoolCollHdl = nil;
  584.     FInfo                theFInfo;
  585.     FSSpec                curFSpec;
  586.     Boolean                wasChanged, isNew = false;
  587.     Str255                prefsFileName;
  588.  
  589. // Try to find our prefs file in the Preferences folder.
  590.     
  591.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  592.     require((err == fnfErr), PrefsFileExists);
  593.  
  594. // No prefs file-- create and open one.
  595.  
  596.     GetPrefsName(&prefsFileName);
  597.     err = FindFolder(kOnSystemDisk,kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  598.     nrequire(err, FindFolder_Failed1);
  599.  
  600.     HDelete(foundVRefNum, foundDirID, prefsFileName);
  601.     HCreateResFile(foundVRefNum, foundDirID, prefsFileName);
  602.     nrequire((err = ResError()), HCreateResFile_Failed);
  603.     
  604.     HGetFInfo(foundVRefNum, foundDirID, prefsFileName, &theFInfo);
  605.     theFInfo.fdType = kPrefsFileType;
  606.     theFInfo.fdCreator = kPrefsFileCreator;
  607.     HSetFInfo(foundVRefNum, foundDirID, prefsFileName, &theFInfo);
  608.  
  609.     resRefNum = HOpenResFile(foundVRefNum, foundDirID, prefsFileName, fsRdWrPerm); 
  610.     nrequire((err = ResError()), HOpenResFile_Failed);
  611.  
  612.  
  613. PrefsFileExists:
  614.  
  615.     oldResRef = CurResFile();
  616.     UseResFile(resRefNum);
  617.  
  618.  
  619. // Get a previously saved or newly created alias to the spool folder.
  620.  
  621.     if (gCurFolderAlias != nil)
  622.         DisposHandle((Handle) gCurFolderAlias);
  623.  
  624.     gCurFolderAlias = (AliasHandle) Get1Resource(rAliasType, kAliasID);
  625.  
  626.     if (gCurFolderAlias != nil)
  627.         DetachResource((Handle) gCurFolderAlias);
  628.     else                                        // no resource-- create one.
  629.     {
  630.         err = FindFolder(kOnSystemDisk, kPrintMonitorDocsFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  631.         nrequire(err, FindFolder_Failed2);
  632.  
  633.         FSMakeFSSpec(foundVRefNum, foundDirID, nil, &curFSpec);
  634.         err = NewAlias(nil, &curFSpec, &gCurFolderAlias);
  635.         nrequire(err, NewAlias_Failed);
  636.  
  637.         err = ReplaceResource((Handle) gCurFolderAlias, rAliasType, kAliasID);
  638.         nrequire(err, ReplaceResource_Failed);
  639.     }
  640.  
  641.  
  642. // Get a previously saved or newly created handle to our panel settings.
  643.  
  644.     spoolCollHdl = (SpoolCollection **) Get1Resource(kConfigType, kConfigID);
  645.  
  646.     if (spoolCollHdl != nil)
  647.         DetachResource((Handle) spoolCollHdl);
  648.     else                                        // no resource-- create one.
  649.     {
  650.         spoolCollHdl = (SpoolCollection **) NewHandle(sizeof(SpoolCollection));
  651.         (*spoolCollHdl)->extTurnedOn = kRedirectDisabled;
  652.         isNew = true;
  653.     }
  654.  
  655.  
  656. // If our alias needs to be updated, update it and save off the new volume
  657. // and folder names.  In any case, move our settings into the passed pointer
  658. // and return them to the caller.
  659.  
  660.     require_action((spoolCollHdl != nil), CanNotCreateSettings, err = MemError(););
  661.  
  662.     HLock((Handle) spoolCollHdl);
  663.     wasChanged = AliasToPathName(gCurFolderAlias, &(*spoolCollHdl)->folderName, &(*spoolCollHdl)->volumeName);
  664.     HUnlock((Handle) spoolCollHdl);
  665.  
  666.     if (wasChanged || isNew)
  667.         err = ReplaceResource((Handle) spoolCollHdl, kConfigType, kConfigID);
  668.  
  669.     BlockMove(*spoolCollHdl, spoolConfig, sizeof(SpoolCollection));
  670.     DisposHandle((Handle) spoolCollHdl);
  671.  
  672.  
  673. CanNotCreateSettings:
  674. ReplaceResource_Failed:
  675. NewAlias_Failed:
  676. FindFolder_Failed2:
  677.  
  678.     UseResFile(oldResRef);
  679.     CloseResFile(resRefNum);
  680.  
  681.  
  682. HOpenResFile_Failed:
  683. HCreateResFile_Failed:
  684. FindFolder_Failed1:
  685.     
  686.     return err;
  687. }
  688.  
  689.  
  690. /*******************************************************************
  691.     OpenPrefsFile opens our preferences file.
  692.  
  693. ********************************************************************/
  694.  
  695. OSErr OpenPrefsFile(short *resRefNum, char permission)
  696. {
  697.     OSErr    err;
  698.     long    foundDirID;
  699.     short    foundVRefNum;
  700.     Boolean    targetIsFolder, wasAliased;
  701.     FSSpec    fSpec;
  702.     Str255    prefsFileName;
  703.  
  704.  
  705. // Find the preferences folder and open our preferences file, if it exists.
  706.  
  707.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  708.  
  709.     if (!err)
  710.     {
  711.         GetPrefsName(&prefsFileName);
  712.         err = FSMakeFSSpec(foundVRefNum, foundDirID, prefsFileName, &fSpec);
  713.         nrequire(err, ThereWasAnError);
  714.  
  715.         err = ResolveAliasFile(&fSpec, true, &targetIsFolder, &wasAliased);
  716.         nrequire(err, ThereWasAnError);
  717.  
  718.         gPrefsVRefNum = fSpec.vRefNum;
  719.         *resRefNum = FSpOpenResFile(&fSpec, permission);
  720.         err = ResError();
  721.     }
  722.  
  723.  
  724. ThereWasAnError:
  725.  
  726.     return err;
  727. }
  728.  
  729.  
  730. /*******************************************************************
  731.     ReplaceResource adds or replaces a resource in the current
  732.     resource file.  (Make sure the file you want to access is
  733.     open and is the current resource file!)  The resource is
  734.     detached when the data is returned, so call DisposHandle on
  735.     it, NOT ReleaseResource.
  736.  
  737. ********************************************************************/
  738.  
  739. OSErr ReplaceResource(Handle newData, OSType resourceType, short resourceID)
  740. {
  741.     OSErr    err;
  742.     Handle    oldResource;
  743.  
  744.     oldResource = Get1Resource(resourceType, resourceID);
  745.     
  746.     require((oldResource != nil), AddingNewResource);
  747.     RmveResource(oldResource);
  748.     DisposHandle(oldResource);
  749.  
  750.  
  751. AddingNewResource:
  752.     
  753.     AddResource(newData, resourceType, resourceID, "\p");
  754.     
  755.     if (!(err = ResError()))
  756.         WriteResource(newData);
  757.  
  758.     UpdateResFile(CurResFile());
  759.     FlushVol(nil, gPrefsVRefNum);
  760.  
  761.     if (!err) DetachResource(newData);
  762.     return err;
  763. }
  764.  
  765.  
  766. /*******************************************************************
  767.     GetSpoolCollection is a handy routine that retrieves our
  768.     SpoolCollection item from the current job collection.
  769.  
  770. ********************************************************************/
  771.  
  772. OSErr GetSpoolCollection(SpoolCollection *spoolCollect)
  773. {
  774.     OSErr        err;
  775.     Collection    jobCollection;
  776.  
  777.     jobCollection = GXGetJobCollection(GXGetJob());
  778.  
  779.     err = GetCollectionItem(jobCollection,
  780.                             kSpoolCollectionType,
  781.                             gxPrintingTagID,
  782.                             nil,
  783.                             spoolCollect);
  784.     return err;
  785. }
  786.  
  787.  
  788. /*******************************************************************
  789.     AliasToPathName extracts the folder and volume name from the
  790.     passed alias, updating them if it needs to.  (I use MatchAlias
  791.     so that I can avoid the user dialogs and volume mounting if
  792.     the alias spans a network.)  If these types of aliases are
  793.     out of date, they will be updated at CreateSpoolFile time.
  794.     We return true if the alias has been updated and needs to be
  795.     saved off.
  796.  
  797. ********************************************************************/
  798.  
  799. Boolean AliasToPathName(AliasHandle anAlias, char *folderName, char *volName)
  800. {
  801.     Boolean        wasChanged;
  802.     OSErr        err;
  803.     FSSpec        fSpec;
  804.     short        num = 1;
  805.  
  806. // See if we can find the folder referenced by the alias.
  807.  
  808.     err = MatchAlias(nil, kARMSearch, anAlias, &num, (FSSpecArrayPtr) &fSpec,
  809.                      &wasChanged, nil, nil);
  810.  
  811.     nrequire_action(err, AliasMgrError, wasChanged = false;);
  812.  
  813.  
  814. // If we found the folder but its path has changed, update the folder and
  815. // volume names referenced.
  816.  
  817.     if (wasChanged)
  818.         err = UpdateAlias(nil, &fSpec, anAlias, &wasChanged);
  819.  
  820.     nrequire(err, AliasMgrError);
  821.     GetAliasInfo(anAlias, asiAliasName, folderName);
  822.     GetAliasInfo(anAlias, asiVolumeName, volName);
  823.  
  824.  
  825. AliasMgrError:
  826.  
  827.     return wasChanged;
  828. }
  829.  
  830.  
  831. /*******************************************************************
  832.     GetPrefsName returns the name of our preferences file.
  833.  
  834. ********************************************************************/
  835.  
  836. void GetPrefsName(char *prefsName)
  837. {
  838.     short    oldResFile;
  839.  
  840. // We just load the name of our preferences file from our extension.
  841.  
  842.     oldResFile = CurResFile();
  843.     UseResFile(GXGetMessageHandlerResFile());
  844.  
  845.     GetIndString(prefsName, r_stringRsrc, r_prefsStrIdx);
  846.     UseResFile(oldResFile);
  847. }
  848.  
  849.